package org.colafries.admin.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.base.Strings;
import lombok.extern.slf4j.Slf4j;
import org.colafries.admin.entity.domain.DataBase;
import org.colafries.admin.entity.domain.DataModel;
import org.colafries.admin.entity.vo.ColumnRspVO;
import org.colafries.admin.entity.vo.DataBaseReqVO;
import org.colafries.admin.entity.vo.DataBaseRspVO;
import org.colafries.admin.exceptions.ServiceException;
import org.colafries.admin.mapper.DataBaseMapper;
import org.colafries.admin.mapper.DataModelMapper;
import org.colafries.admin.service.IDataBaseService;
import org.colafries.common.metadata.Column;
import org.colafries.common.metadata.Schema;
import org.colafries.common.utils.HashUtil;
import org.colafries.metadata.driver.Driver;
import org.colafries.metadata.entity.domain.DriverConfig;
import org.colafries.metadata.factory.DriverFactory;
import org.colafries.metadata.result.JdbcSelectResult;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author colafries
 * @version v1.0
 * @ClassName DataSourceServiceImpl
 * @Description
 * @date Created in 2024/3/14
 **/
@Service
@Slf4j
public class IDataBaseServiceImpl extends ServiceImpl<DataBaseMapper, DataBase> implements IDataBaseService {
    @Resource
    DataBaseMapper dataBaseMapper;
    @Resource
    DriverFactory driverFactory;
    @Resource
    DataModelMapper dataModelMapper;

    /**
     * 查询数据源
     *
     * @param dataBaseReqVO
     * @return
     */
    @Override
    public List<DataBaseRspVO> getAll(DataBaseReqVO dataBaseReqVO) {
        List<DataBase> dataBaseList = dataBaseMapper.selectList(
                Wrappers.<DataBase>lambdaQuery().orderByDesc(DataBase::getUpdateTime)
        );
        if (!CollectionUtil.isEmpty(dataBaseList)) {
            return dataBaseList.stream()
                    .map(dataBase -> DataBaseRspVO.builder()
                            .dataBaseId(dataBase.getId())
                            .dbName(dataBase.getDbName())
                            .dbTypeCode(dataBase.getDbTypeCode())
                            .dbHost(dataBase.getDbHost())
                            .dbPort(dataBase.getDbPort())
                            .dbUrlArgs(dataBase.getDbUrlArgs())
                            .dbUser(dataBase.getDbUser())
                            .dbDesc(dataBase.getDbDesc())
                            .updateUser(dataBase.getUpdateUser())
                            .build())
                    .collect(Collectors.toList());
        }
        return CollectionUtil.newArrayList();
    }

    /**
     * 修改数据源
     *
     * @param dataBaseReqVO
     * @return
     */
    @Override
    public int update(DataBaseReqVO dataBaseReqVO) {
        int cnt = dataBaseMapper.selectCount(
                Wrappers.<DataBase>lambdaQuery().eq(DataBase::getId, dataBaseReqVO.getDatabaseId())
        );
        if (cnt == 0) {
            log.info("数据源id {} 不存在", dataBaseReqVO.getDatabaseId());
            return 0;
        }
        DataBase dataBase = new DataBase();
        BeanUtils.copyProperties(dataBaseReqVO, dataBase);
        //补充属性
        dataBase.setId(dataBaseReqVO.getDatabaseId());
        return dataBaseMapper.updateById(dataBase);
    }

    /**
     * 新增数据源
     *
     * @param dataBaseReqVO
     * @return
     */
    @Override
    public int add(DataBaseReqVO dataBaseReqVO) {
        //判断同名数据源
        DataBase existsDataBase = dataBaseMapper.selectOne(
                Wrappers.<DataBase>lambdaQuery()
                        .eq(DataBase::getDbName, dataBaseReqVO.getDbName())
                        // 剔除当前数据源
                        .ne(DataBase::getId, dataBaseReqVO.getDatabaseId())
        );
        if (existsDataBase != null) {
            log.info("存在同名数据源");
            return 0;
        }

        DataBase dataBase = new DataBase();
        BeanUtils.copyProperties(dataBaseReqVO, dataBase);
        return dataBaseMapper.insert(dataBase);
    }

    /**
     * 删除数据源
     *
     * @param reqVO
     * @return
     */
    @Override
    public int delete(DataBaseReqVO reqVO) {
        // 判断数据源是否存储
        Integer databaseCnt = this.lambdaQuery().eq(DataBase::getId, reqVO.getDatabaseId()).count();
        if (0 == databaseCnt) {
            throw new RuntimeException("数据源删除失败，数据源不存在 数据源id " + reqVO.getDatabaseId());
        }
        //判断是否有模型引用
        LambdaQueryWrapper<DataModel> qw = new LambdaQueryWrapper<>();
        qw.eq(DataModel::getDatabaseId, reqVO.getDatabaseId());
        int dataModelCnt = dataModelMapper.selectCount(qw);
        if (dataModelCnt > 0) {
            log.info("数据源被数据模型引用，无法删除。");
            throw new RuntimeException("数据源被数据模型引用，无法删除。");
        }
        return dataBaseMapper.deleteById(reqVO.getDatabaseId());
    }

    /**
     * 测试数据源联通性
     *
     * @param reqVO
     * @return
     */
    @Override
    public Map<String, String> testConnection(DataBaseReqVO reqVO) {
        Map<String, String> resultMap = MapUtil.newHashMap();
        DataBase dataBase = new DataBase().setDbTypeCode(reqVO.getDbTypeCode()).setDbHost(reqVO.getDbHost())
                .setDbPort(reqVO.getDbPort()).setDbUser(reqVO.getDbUser()).setDbPwd(reqVO.getDbPwd());
        DriverConfig driverConfig = buildDriverConfig(dataBase);
        Driver driver = driverFactory.getDriver(dataBase.getDbTypeCode()).build(driverConfig);
        try {
            Boolean result = driver.test(dataBase.getDbUser(), dataBase.getDbPwd());
            resultMap.put("connectFlag", result.toString());
        } catch (Exception e) {
            resultMap.put("connectFlag", "false");
            resultMap.put("connectMsg", e.getMessage());
        }
        return resultMap;
    }

    /**
     * 获取数据源的库和表
     *
     * @param reqVO
     * @return
     */
    @Override
    public List<Schema> getSchemasAndTables(DataBaseReqVO reqVO) {
        DataBase dataBase = dataBaseMapper.selectById(reqVO.getDatabaseId());
        if (dataBase == null) {
            throw new RuntimeException("数据源不存在 数据源id: " + reqVO.getDatabaseId());
        }
        DriverConfig driverConfig = buildDriverConfig(dataBase);
        Driver driver = driverFactory.getDriver(dataBase.getDbTypeCode()).build(driverConfig);
        List<Schema> schemas = driver.listSchemas();
        if (StrUtil.isNotEmpty(reqVO.getSchemaName())) {
            schemas = schemas.stream().filter(schema -> schema.getSchemaName().equals(reqVO.getSchemaName()))
                    .collect(Collectors.toList());
        }

        return schemas.stream().peek(schema -> {
            schema.setTables(driver.listTables(schema.getSchemaName()));
        }).collect(Collectors.toList());
    }

    /**
     * getTableSchema
     *
     * @param dataBaseReqVO
     * @return java.util.List<org..model.Column>
     * @author colafries
     * @Date 2024/3/21
     */
    @Override
    public List<Column> getTableSchema(DataBaseReqVO dataBaseReqVO) {
        DataBase dataBase = dataBaseMapper.selectById(dataBaseReqVO.getDatabaseId());
        if (dataBase == null) {
            throw new RuntimeException("数据源为空");
        }
        DriverConfig driverConfig = buildDriverConfig(dataBase);
        Driver driver = driverFactory.getDriver(dataBase.getDbTypeCode()).build(driverConfig);
        // schema
        String schemaName = dataBaseReqVO.getSchemaName();
        if (Strings.isNullOrEmpty(schemaName)) {
            throw new RuntimeException("schemaName为空");
        }
        // table
        String tableName = dataBaseReqVO.getTableName();
        if (Strings.isNullOrEmpty(tableName)) {
            throw new RuntimeException("tableName为空");
        }
        return driver.getTableSchema(schemaName, tableName);
    }

    /***
     * 查询sql结果
     * @param querySql 查询数据
     * @return org..colafries.admin.metadata.result.JdbcSelectResult
     * @author colafries
     * @Date 2024/3/24
     */
    @Override
    public JdbcSelectResult querySqlPage(Integer databaseId, String querySql, Integer current, Integer size) {
        DataBase dataBase = dataBaseMapper.selectById(databaseId);
        if (dataBase == null) {
            throw new RuntimeException("数据源为空");
        }
        if (Strings.isNullOrEmpty(querySql)) {
            throw new RuntimeException("sql为空");
        }
        DriverConfig driverConfig = buildDriverConfig(dataBase);
        Driver driver = driverFactory.getDriver(dataBase.getDbTypeCode()).build(driverConfig);
        return driver.queryPage(querySql, current, size);
    }

    /**
     * 查询sql结果schema
     *
     * @param queryDataReqVO
     * @return java.util.List<org..colafries.admin.entity.vo.ColumnRspVO>
     * @author colafries
     * @Date 2024/4/3
     */
    @Override
    public List<ColumnRspVO> querySqlSchema(Integer databaseId, String querySql) {
        DataBase dataBase = dataBaseMapper.selectById(databaseId);
        if (dataBase == null) {
            throw new ServiceException("数据源为空");
        }
        DriverConfig driverConfig = buildDriverConfig(dataBase);
        Driver driver = driverFactory.getDriver(dataBase.getDbTypeCode()).build(driverConfig);
        List<Column> columnList = driver.querySchema(querySql);
        if (!CollectionUtil.isEmpty(columnList)) {
            return columnList.stream().map(column -> ColumnRspVO.builder()
                            .field(column.getName())
                            // type 转小写
                            .dbType(column.getType().toLowerCase())
                            // javaType 转小写 首字母大写
                            .javaType(column.getJavaTypeFormat())
                            .fieldHash(HashUtil.genFieldHash(column.getName()))
                            .build())
                    .collect(Collectors.toList());
        }
        return CollectionUtil.newArrayList();
    }

    /***
     * 构建驱动配置
     * @param dataBase
     * @return org..metadata.entity.domain.DriverConfig
     * @author colafries
     * @Date 2024/3/22
     */
    public DriverConfig buildDriverConfig(DataBase dataBase) {
        return DriverConfig.builder()
                .dataSourceKey(dataBase.getKey())
                .userName(dataBase.getDbUser())
                .passWord(dataBase.getDbPwd())
                .url(dataBase.getUrl())
                .type(dataBase.getDbTypeCode())
                .build();
    }
}
